package com.atguigu.yygh.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.exphandler.YyghException;
import com.atguigu.mq.consts.MqConst;
import com.atguigu.mq.service.RabbitService;
import com.atguigu.utils.HttpRequestHelper;
import com.atguigu.yygh.enums.OrderStatusEnum;
import com.atguigu.yygh.hosp.client.HospFeignClient;
import com.atguigu.yygh.model.order.OrderInfo;
import com.atguigu.yygh.model.user.Patient;
import com.atguigu.yygh.order.mapper.OrderInfoMapper;
import com.atguigu.yygh.order.service.OrderInfoService;
import com.atguigu.yygh.order.service.WeixinService;
import com.atguigu.yygh.user.client.PatientFeignClient;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import com.atguigu.yygh.vo.msm.MsmVo;
import com.atguigu.yygh.vo.order.OrderCountQueryVo;
import com.atguigu.yygh.vo.order.OrderCountVo;
import com.atguigu.yygh.vo.order.OrderMqVo;
import com.atguigu.yygh.vo.order.OrderQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2023-06-17
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {


    @Autowired
    PatientFeignClient patientFeignClient;

    @Autowired
    HospFeignClient hospFeignClient;

    @Autowired
    RabbitService rabbitService;//rabbit_util

    @Autowired
    WeixinService weixinService;

    @Override
    public Long submitOrder(String scheduleId, Long patientId) {

        //远程服务调用，准备数据，为了后续的调用医院端接口 和 创建平台端订单做准备
        Patient patient = patientFeignClient.getPatientById(patientId);
        ScheduleOrderVo scheduleOrderVo = hospFeignClient.getScheduleOrderVo(scheduleId);//ScheduleOrderVo=排班基本信息+4个time时间


        //调用医院端接口时使用的参数，创建平台端订单时使用的数据

        //`yygh_hosp`.`hospital_set`  从尚医通平台数据库中，查询该医院的api_url
        String apiUrl = hospFeignClient.getApiUrl(scheduleOrderVo.getHoscode());//医院端接口的 http://ip:port ,不同的医院，地址不一样

        if(!apiUrl.startsWith("http")){
            apiUrl = "http://" + apiUrl;
        }

        //1、调用医院端的接口（5.1预约下单接口）  注意：hospital-manage项目下的接口，一定启动他！！！
        //1.1  医院端接口地址
        String url = apiUrl+"/order/submitOrder";

        //1.2 封装参数
        Map<String,Object> map = new HashMap<>();
        map.put("hoscode",scheduleOrderVo.getHoscode());
        map.put("depcode",scheduleOrderVo.getDepcode());
        map.put("hosScheduleId",scheduleOrderVo.getHosScheduleId());//
        map.put("reserveDate",scheduleOrderVo.getReserveDate());
        map.put("reserveTime",scheduleOrderVo.getReserveTime());
        map.put("amount",scheduleOrderVo.getAmount());
        map.put("name",patient.getName());
        map.put("sex",patient.getSex());
        map.put("birthdate",patient.getBirthdate());
        map.put("phone",patient.getPhone());
        map.put("isMarry",patient.getIsMarry());
        map.put("provinceCode",patient.getProvinceCode());
        map.put("cityCode",patient.getCityCode());
        map.put("districtCode",patient.getDistrictCode());
        map.put("address",patient.getAddress());
        map.put("contactsName",patient.getContactsName());
        map.put("contactsCertificatesType",patient.getContactsCertificatesType());
        map.put("contactsCertificatesNo",patient.getContactsCertificatesNo());
        map.put("contactsPhone",patient.getContactsPhone());
        map.put("isInsure",patient.getIsInsure());

        //真正发请求，调用医院端的预约下单接口，会在医院端数据库中创建一个订单（`yygh_manage`.`order_info`）
        //并且还要将医院端自己的数据库中的排班表找到对应的排班进行库存数量的扣减`yygh_manage`.`schedule`
        JSONObject jsonObject = HttpRequestHelper.sendRequest(map, url);

        Integer code = jsonObject.getInteger("code");
        if(code!=200){
            //有可能没有启动hospital-manage
            throw new YyghException(20001,"医院端预约下单接口调用失败");
        }

        JSONObject data = jsonObject.getJSONObject("data");

        Long hosRecordId = data.getLong("hosRecordId");//医院端创建的订单的id
        Integer number = data.getInteger("number");//预约序号
        //这两个num，更新到mongodb中
        Integer reservedNumber = data.getInteger("reservedNumber");//医院端该排班最新的总号源数量
        Integer availableNumber = data.getInteger("availableNumber");//医院端该排班最新的剩余号源数量
        String fetchTime = data.getString("fetchTime");
        String fetchAddress = data.getString("fetchAddress");

        //2、创建平台端订单 `yygh_order`.`order_info`
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(patient.getUserId());//用户id

        String outTradeNo = System.currentTimeMillis() +""+ new Random().nextInt(100);
//        String outTradeNo = UUID.randomUUID().toString().replaceAll("-", ""); //截取到30个长度以内
        orderInfo.setOutTradeNo(outTradeNo);//订单交易号(订单编号),保证唯一性

        orderInfo.setHoscode(scheduleOrderVo.getHoscode());
        orderInfo.setHosname(scheduleOrderVo.getHosname());
        orderInfo.setDepcode(scheduleOrderVo.getDepcode());
        orderInfo.setDepname(scheduleOrderVo.getDepname());
        orderInfo.setTitle(scheduleOrderVo.getTitle());
        orderInfo.setScheduleId(scheduleId);//mongodb中的排班的id
        orderInfo.setReserveDate(scheduleOrderVo.getReserveDate());
        orderInfo.setReserveTime(scheduleOrderVo.getReserveTime());
        orderInfo.setPatientId(patient.getId());
        orderInfo.setPatientName(patient.getName());
        orderInfo.setPatientPhone(patient.getPhone());

        orderInfo.setHosRecordId(hosRecordId+"");//医院端订单id
        orderInfo.setNumber(number);//预约序号
        orderInfo.setFetchTime(fetchTime);//取号时间
        orderInfo.setFetchAddress(fetchAddress);//取号地址

        orderInfo.setAmount(scheduleOrderVo.getAmount());//挂号费

        orderInfo.setQuitTime(scheduleOrderVo.getQuitTime());//退号截止时间
        orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());//订单状态 0-预约成功未支付

        baseMapper.insert(orderInfo);
        //添加完成后，获取到订单id主键
        Long id = orderInfo.getId();


        //3、使用mq的方式，异步给就诊人（or用户）发送短信通知； 异步更新mongodb中该排班的两个num
        OrderMqVo orderMqVo = new OrderMqVo();
        orderMqVo.setReservedNumber(reservedNumber);
        orderMqVo.setAvailableNumber(availableNumber);
        orderMqVo.setScheduleId(scheduleId);//mongodb中排班id

        //封装短信消息：phone + param.message
        MsmVo msmVo = new MsmVo();
        msmVo.setPhone(patient.getPhone());//手机号（用户or就诊人）
        msmVo.getParam().put("message","【尚医通】订单已创建！");


        orderMqVo.setMsmVo(msmVo);//将来医院服务向第二个队列中发送的消息


        //参数1：第一个交换机名称  参数2：第一个key  参数3：消息对象
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,orderMqVo);


        return id;

    }

    @Override
    public OrderInfo getOrderById(Long orderId) {
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        this.packOrderInfo(orderInfo);
        return orderInfo;
    }

    @Override
    public Page<OrderInfo> selectPage(Integer pageNum, Integer pageSize, OrderQueryVo orderQueryVo) {

        //mybatis的带条件的分页查询
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();

        Long patientId = orderQueryVo.getPatientId();
        String orderStatus = orderQueryVo.getOrderStatus();
        Long userId = orderQueryVo.getUserId();

        if(!StringUtils.isEmpty(orderStatus)){
            queryWrapper.eq("order_status",orderStatus);
        }
        if(!StringUtils.isEmpty(patientId)){
            queryWrapper.eq("patient_id",patientId);
        }
        if(!StringUtils.isEmpty(userId)){
            queryWrapper.eq("user_id",userId);
        }


        Page<OrderInfo> orderInfoPage = new Page<>(pageNum,pageSize);//1-第一页

        baseMapper.selectPage(orderInfoPage, queryWrapper);


        orderInfoPage.getRecords().forEach(orderInfo -> this.packOrderInfo(orderInfo));


        return orderInfoPage;
    }

    @Override
    public void cancelOrder(Long orderId) {
        //判断订单是否能够取消
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        //判空校验
        if (orderInfo==null){
            throw new YyghException(20001,"订单不存在");
        }
        //判断订单的状态如果为已取消，不执行后续
        Integer orderStatus = orderInfo.getOrderStatus();
        if (orderStatus == OrderStatusEnum.CANCLE.getStatus()){
            throw new YyghException(20001,"订单已取消，请勿重复操作");
        }

        //判断订单的退号截止时间
        Date quitTime = orderInfo.getQuitTime();//退号截止时间
        DateTime dateTime = new DateTime(quitTime);
        if (dateTime.isBeforeNow()){
            throw new YyghException(20001,"订单已超时，无法取消");
        }

        //调用医院端的取消预约接口；   调用医院服务，根据hoscode查询医院设置中的apiUrl医院端接口的地址
        String url =  hospFeignClient.getApiUrl(orderInfo.getHoscode()) + "/order/updateCancelStatus";
        //封装医院端该接口需要的参数
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("hoscode",orderInfo.getHoscode());//医院编号
        paramMap.put("hosRecordId",orderInfo.getHosRecordId());//医院端订单的id

        //医院端的接口使用HttpRequestHelper工具类调用
        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, url);
        //医院端接口的返回值中code！=200 说明调用失败了
        Integer code = jsonObject.getInteger("code");
        if (code!=200){
            throw new YyghException(20001,"医院端取消预约接口调用失败");
        }

        //判断订单的状态如果是已支付，开始执行退款流程
        if (orderInfo.getOrderStatus() == OrderStatusEnum.PAID.getStatus()){
            //退款流程
            weixinService.refund(orderId);
        }

        //平台端的订单状态改成-1（已取消）
        orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
        baseMapper.updateById(orderInfo);

        //  使用mq异步实现mg中排班availableNumbeer+1  ， 给就诊人发送短信通知
        MsmVo msmVo = new MsmVo();
        msmVo.setPhone(orderInfo.getPatientPhone());
        msmVo.getParam().put("message","【尚医通】订单已取消");

        OrderMqVo orderMqVo = new OrderMqVo();
        orderMqVo.setMsmVo(msmVo);
        //mongodb中的排班id
        orderMqVo.setScheduleId(orderInfo.getScheduleId());

        //向第一个队列中发送消息（service_hosp医院服务监听）
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,orderMqVo);
    }

    @Override
    public Map<String, Object> getCountMap(OrderCountQueryVo orderCountQueryVo) {
        //筛选订单，进行统计（日期对应的订单数量）
        List<OrderCountVo> list = baseMapper.selectOrderCount(orderCountQueryVo);

        List<String> dateList = list.stream().map(OrderCountVo::getReserveDate).collect(Collectors.toList());;
        List<Integer> countList = list.stream().map(OrderCountVo::getCount).collect(Collectors.toList());



        Map<String,Object> map = new HashMap<>();
        map.put("dateList",dateList);//日期集合
        map.put("countList",countList);//数量集合


        return map;
    }

    private void packOrderInfo(OrderInfo orderInfo) {
        String statusNameByStatus = OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus());
        orderInfo.getParam().put("orderStatusString",statusNameByStatus);
    }
}
